<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * This file contains the following classes:
 *
 * - Crypto
 * - Crypto_Exception
 * - Crypto_CipherUnavailableException
 * - Crypto_ModeUnavailableException
 *
 * PHP version 5
 *
 * This file is part of Majic - A rapid-development web application framework
 * for PHP 5.
 *
 * Majic is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * Majic is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Majic. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package     Crypto
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @copyright   2007-2008 Michael J. I. Jackson
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
 * @version     SVN: $Id: Crypto.php 36 2008-08-09 23:31:49Z mjijackson $
 */

/**
 * A wrapper class for the Mcrypt encryption methods. Requires the Mcrypt PHP
 * extension.
 *
 * @package     Crypto
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 */
class Crypto
{

    /**
     * The encryption cipher to use.
     *
     * @var     int
     * @access  private
     */
    private $cipher;

    /**
     * The encryption mode to use.
     *
     * @var     int
     * @access  private
     */
    private $mode;

    /**
     * Constructor.
     *
     * @throws  Crypto_Exception        If the Mcrypt extension is not loaded
     * @access  public
     */
    public function __construct()
    {
        if (!extension_loaded('mcrypt')) {
            throw new Crypto_Exception('No Mcrypt library found');
        }
    }

    /**
     * Encrypts a string of data.
     *
     * @param   string  $key        The encryption key
     * @param   string  $data       The data to encrypt
     * @return  string              The encrypted data
     * @access  public
     */
    public function encrypt($key, $data)
    {
        $iv = $this->createIV();
        $cipher = $this->getCipher();
        $mode = $this->getMode();

        return mcrypt_encrypt($cipher, $key, $data, $mode, $iv);
    }

    /**
     * Decrypts a string of data.
     *
     * @param   string  $key        The encryption key
     * @param   string  $data       The data to decrypt
     * @return  string              The decrypted data
     * @access  public
     */
    public function decrypt($key, $data)
    {
        $iv = $this->createIV();
        $cipher = $this->getCipher();
        $mode = $this->getMode();

        return mcrypt_decrypt($cipher, $key, $data, $mode, $iv);
    }

    /**
     * Creates an IV for use in the encryption.
     *
     * @return  string              The IV to use in the encryption
     * @access  private
     */
    private function createIV()
    {
        $cipher = $this->getCipher();
        $mode = $this->getMode();

        $iv_size = mcrypt_get_iv_size($cipher, $mode);
        return mcrypt_create_iv($iv_size, MCRYPT_RAND);
    }

    /**
     * Sets the cipher to use.
     *
     * @param   string  $cipher     One of the MCRYPT_* cipher constants
     * @return  void
     * @throws  Crypto_CipherUnavailableException   If the cipher is not
     *                                              available
     * @access  public
     */
    public function setCipher($cipher)
    {
        if (!in_array($cipher, mcrypt_list_algorithms())) {
            throw new Crypto_CipherUnavailableException($cipher);
        }

        $this->cipher = $cipher;
    }

    /**
     * Gets the cipher in use. Defaults to MCRYPT_RIJNDAEL_256 if none has been
     * set.
     *
     * @return  string              The cipher constant in use
     * @access  public
     */
    public function getCipher()
    {
        if (isset($this->cipher)) {
            return $this->cipher;
        }

        return MCRYPT_RIJNDAEL_256;
    }

    /**
     * Sets the mode to use.
     *
     * @param   string  $mode       One of the MCRYPT_MODE_* constants
     * @return  void
     * @throws  Crypto_ModeUnavailableException     If the mode is not
     *                                              available
     * @access  public
     */
    public function setMode($mode)
    {
        if (!in_array($mode, mcrypt_list_modes())) {
            throw new Crypto_ModeUnavailableException($mode);
        }

        $this->mode = $mode;
    }

    /**
     * Gets the encryption mode. Defaults to MCRYPT_MODE_ECB if none has been
     * set.
     *
     * @return  string              The mode constant in use
     * @access  public
     */
    public function getMode()
    {
        if (isset($this->mode)) {
            return $this->mode;
        }

        return MCRYPT_MODE_ECB;
    }

}

/**
 * Crypto exception. The base exception class for all Crypto exceptions.
 *
 * @package     Crypto
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 */
class Crypto_Exception extends Majic_Exception {}

/**
 * Crypto cipher unavailable exception. Thrown when the given cipher is not
 * available.
 *
 * @package     Crypto
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 */
class Crypto_CipherUnavailableException extends Crypto_Exception
{

    /**
     * Constructor.
     *
     * @param   int     $cipher         The cipher code
     * @access  public
     */
    public function __construct($cipher)
    {
        parent::__construct("Mcrypt cipher not available ($cipher)");
    }

}

/**
 * Crypto mode unavailable exception. Thrown when the given mode is not
 * available.
 *
 * @package     Crypto
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 */
class Crypto_ModeUnavailableException extends Crypto_Exception
{

    /**
     * Constructor.
     *
     * @param   int     $mode           The mode code
     * @access  public
     */
    public function __construct($mode)
    {
        parent::__construct("Mcrypt mode not available ($mode)");
    }

}

?>
